import * as fs from 'fs-extra';
import * as path from 'path';
import * as crypto from 'crypto';
import { ConfigManager } from './ConfigManager';
import { TencentCloudService } from './cloud/TencentCloudService';
import { AliyunCloudService } from './cloud/AliyunCloudService';
import { BytedanceCloudService } from './cloud/BytedanceCloudService';
import { AWSCloudService } from './cloud/AWSCloudService';
import { HuaweiCloudService } from './cloud/HuaweiCloudService';
import { FileInfo, ComparisonResult, CloudConfig } from './types';
import { Logger } from '../utils/Logger';

// 本地文件信息接口
interface LocalFileInfo {
  path: string;
  size: number;
  lastModified: Date;
  hash: string;
  relativePath: string;
}

export interface CompareResult {
  newFiles: LocalFileInfo[];
  modifiedFiles: LocalFileInfo[];
  unchangedFiles: LocalFileInfo[];
  totalFiles: number;
  totalSize: number;
}

export class FileComparer {
  /**
   * 比较本地文件和远程文件
   */
  async compareFiles(
    localPath: string,
    remotePath: string,
    provider: string
  ): Promise<CompareResult> {
    try {
      // 获取本地文件列表
      const localFiles = await this.getLocalFiles(localPath);
      
      // 获取远程文件列表
      const remoteFiles = await this.getRemoteFiles(remotePath, provider);
      
      // 比较文件
      return this.performComparison(localFiles, remoteFiles);
    } catch (error) {
      console.error('文件比较失败:', error);
      throw error;
    }
  }

  /**
   * 获取本地文件列表
   */
  private async getLocalFiles(localPath: string): Promise<LocalFileInfo[]> {
    const files: LocalFileInfo[] = [];
    
    const scanDirectory = async (dirPath: string, basePath: string) => {
      const items = await fs.readdir(dirPath);
      
      for (const item of items) {
        const fullPath = path.join(dirPath, item);
        const stat = await fs.stat(fullPath);
        
        if (stat.isDirectory()) {
          await scanDirectory(fullPath, basePath);
        } else {
          const relativePath = path.relative(basePath, fullPath).replace(/\\/g, '/');
          const hash = await this.calculateFileHash(fullPath);
          
          files.push({
            path: fullPath,
            size: stat.size,
            lastModified: stat.mtime,
            hash,
            relativePath
          });
        }
      }
    };
    
    await scanDirectory(localPath, localPath);
    return files;
  }

  /**
   * 获取远程文件列表
   */
  private async getRemoteFiles(remotePath: string, provider: string): Promise<Map<string, FileInfo>> {
    const remoteFiles = new Map<string, FileInfo>();
    
    try {
      Logger.info(`开始获取远程文件列表，提供商: ${provider}, 远程路径: ${remotePath}`);
      
      // 获取云服务配置
      const configManager = new ConfigManager();
      const cloudConfig = await configManager.getConfig(provider);
      
      if (!cloudConfig) {
        console.warn(`未找到 ${provider} 的配置，将视为远程无文件`);
        return remoteFiles;
      }
      
      Logger.info(`成功获取 ${provider} 的配置，bucket: ${cloudConfig.bucket}, region: ${cloudConfig.region}`);
      
      let cloudService;
      
      switch (provider) {
          case 'tencent':
            cloudService = new TencentCloudService(cloudConfig);
            break;
          case 'aliyun':
            cloudService = new AliyunCloudService(cloudConfig);
            break;
          case 'bytedance':
            cloudService = new BytedanceCloudService(cloudConfig);
            break;
          case 'aws':
            cloudService = new AWSCloudService(cloudConfig);
            break;
          case 'huawei':
            cloudService = new HuaweiCloudService(cloudConfig);
            break;
        default:
            throw new Error(`不支持的云服务提供商: ${provider}`);
      }
      
      Logger.info(`正在调用 ${provider} 云服务的 listFiles 方法...`);
      const files = await cloudService.listFiles(remotePath);
      Logger.info(`${provider} 云服务返回了 ${files.length} 个文件`);
      
      files.forEach(file => {
        // 处理远程文件key：移除可能的前缀路径，只保留相对路径
        let normalizedKey = file.key;
        
        // 如果远程路径有前缀，需要移除前缀部分
        if (remotePath && remotePath !== '/' && remotePath !== '') {
          const prefix = remotePath.startsWith('/') ? remotePath.substring(1) : remotePath;
          if (normalizedKey.startsWith(prefix)) {
            normalizedKey = normalizedKey.substring(prefix.length);
            // 移除开头的斜杠
            if (normalizedKey.startsWith('/')) {
              normalizedKey = normalizedKey.substring(1);
            }
          }
        }
        
        remoteFiles.set(normalizedKey, file);
      });
      
      Logger.info(`成功处理远程文件列表，共 ${remoteFiles.size} 个文件`);
    } catch (error) {
      console.error('获取远程文件列表失败，详细错误信息:', error);
      console.warn('将视为远程无文件');
    }
    
    return remoteFiles;
  }

  /**
   * 执行文件比较
   */
  private performComparison(localFiles: LocalFileInfo[], remoteFiles: Map<string, FileInfo>): CompareResult {
    const newFiles: LocalFileInfo[] = [];
    const modifiedFiles: LocalFileInfo[] = [];
    const unchangedFiles: LocalFileInfo[] = [];
    
    let totalSize = 0;
    
    Logger.info(`开始比较文件，本地文件数: ${localFiles.length}, 远程文件数: ${remoteFiles.size}`);
    
    for (const localFile of localFiles) {
      const remoteFile = remoteFiles.get(localFile.relativePath);
      
      if (!remoteFile) {
        // 新文件
        newFiles.push(localFile);
        totalSize += localFile.size;
      } else {
        // 获取远程文件的哈希值
        let remoteHash = '';
        if (remoteFile.etag) {
          // 移除ETag的引号和可能的后缀（如-1）
          remoteHash = remoteFile.etag.replace(/"/g, '').split('-')[0];
        } else if (remoteFile.md5) {
          remoteHash = remoteFile.md5;
        }
        
        if (localFile.hash !== remoteHash) {
          // 修改的文件
          modifiedFiles.push(localFile);
          totalSize += localFile.size;
        } else {
          // 未修改的文件
          unchangedFiles.push(localFile);
        }
      }
    }
    
    Logger.info(`比较结果: 新文件 ${newFiles.length}, 修改文件 ${modifiedFiles.length}, 未修改文件 ${unchangedFiles.length}`);
    
    return {
      newFiles,
      modifiedFiles,
      unchangedFiles,
      totalFiles: localFiles.length,
      totalSize
    };
  }

  /**
   * 计算文件哈希值
   */
  private async calculateFileHash(filePath: string): Promise<string> {
    return new Promise((resolve, reject) => {
      const hash = crypto.createHash('md5');
      const stream = fs.createReadStream(filePath);
      
      stream.on('data', (data) => {
        hash.update(data);
      });
      
      stream.on('end', () => {
        resolve(hash.digest('hex'));
      });
      
      stream.on('error', (error) => {
        reject(error);
      });
    });
  }

  /**
   * 格式化文件大小
   */
  static formatFileSize(bytes: number): string {
    const units = ['B', 'KB', 'MB', 'GB', 'TB'];
    let size = bytes;
    let unitIndex = 0;
    
    while (size >= 1024 && unitIndex < units.length - 1) {
      size /= 1024;
      unitIndex++;
    }
    
    return `${size.toFixed(2)} ${units[unitIndex]}`;
  }
}